package com.chenwc.httputil;

import com.chenwc.enums.RequestMethodEnum;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class RestMock {

    /**
     * 连接超时时间，30秒
     */
    private static final int CONNECT_TIME_OUT = 60 * 1000;
    /**
     * 读取超时时间，30秒
     */
    private static final int READ_TIME_OUT = 60 * 1000;

    private static final Logger log = LoggerFactory.getLogger(RestMock.class);

    /**
     * 发送post请求，默认超时时间30秒
     *
     * @param url           请求路径
     * @param hearderParams 请求头，如Content-type、Content-Length等
     * @param body          请求体，如json、xml、text、html等
     * @return 响应信息
     */
    public static String sendPost(String url,
                                  Map<String, String> hearderParams, String body) {
        return getResultByHttp(RequestMethodEnum.POST, url, hearderParams, body, READ_TIME_OUT, CONNECT_TIME_OUT);
    }

    /**
     * 发送post请求，默认超时时间30秒
     *
     * @param url           请求路径
     * @param hearderParams 请求头，如Content-type、Content-Length等
     * @return 响应信息
     */
    public static String sendPost(String url,
                                  Map<String, String> hearderParams) {
        return getResultByHttp(RequestMethodEnum.POST, url, hearderParams, "", READ_TIME_OUT, CONNECT_TIME_OUT);
    }

    /**
     * 发送post请求，默认超时时间30秒
     *
     * @param url 请求路径
     * @return 响应信息
     */
    public static String sendPost(String url) {
        return getResultByHttp(RequestMethodEnum.POST, url, new HashMap<>(), "", READ_TIME_OUT, CONNECT_TIME_OUT);
    }

    /**
     * 发送get请求，默认超时时间30秒
     *
     * @param url 请求路径
     * @return 响应信息
     */
    public static String sendGet(String url) {
        return getResultByHttp(RequestMethodEnum.GET, url, new HashMap<>(), "", READ_TIME_OUT, CONNECT_TIME_OUT);
    }

    /**
     * 发送get请求，默认超时时间30秒
     *
     * @param url           请求路径
     * @param hearderParams 请求头，如Content-type、Content-Length等
     * @return 响应信息
     */
    public static String sendGet(String url,
                                 Map<String, String> hearderParams) {
        return getResultByHttp(RequestMethodEnum.GET, url, hearderParams, "", READ_TIME_OUT, CONNECT_TIME_OUT);
    }

    /**
     * 发送get请求，默认超时时间30秒
     *
     * @param url           请求路径
     * @param hearderParams 请求头，如Content-type、Content-Length等
     * @param parameterMap  请求参数
     * @return 响应信息
     */
    public static String sendGet(String url,
                                 Map<String, String> hearderParams, Map<String, String> parameterMap) {
        String paramerterString = generateGetParameter(parameterMap);
        if (StringUtils.isNotEmpty(paramerterString)) {
            if (url.endsWith("?")) {
                url = url + paramerterString;
            } else {
                url = url + "?" + paramerterString;
            }
        }
        return getResultByHttp(RequestMethodEnum.GET, url, hearderParams, "", READ_TIME_OUT, CONNECT_TIME_OUT);
    }

    /**
     * 发送请求
     *
     * @param requestMethod  请求方式
     * @param url            请求路径
     * @param hearderParams  请求头，如Content-type、Content-Length等
     * @param body           请求体，如json、xml、text、html等
     * @param connectTimeOut 连接超时时间
     * @param readTimeOut    读取响应超时时间
     * @return 响应信息
     */
    public static String getResultByHttp(RequestMethodEnum requestMethod, String url,
                                         Map<String, String> hearderParams, String body, int connectTimeOut, int readTimeOut) {

        log.info("请求url------------->: {}", url);
        log.info("请求方法------------>: {}", requestMethod.getRequestMethod());
        if (null != hearderParams && !hearderParams.isEmpty()) {
            for (String key : hearderParams.keySet()) {
                log.info("添加的请求头信息---------->: {}:{}", key, hearderParams.get(key));
            }
        }
        if (StringUtils.isNotEmpty(body)) {
            log.info("请求体-------------->: {}", body);
        }
        log.info("连接超时时间-------->: {} ms", connectTimeOut);
        log.info("读取响应超时时间----->: {} ms", readTimeOut);
        HttpURLConnection conn = null;
        OutputStream output = null;
        InputStream inputStream = null;
        // 定义返回结果
        String result = "";
        try {
            long start = System.currentTimeMillis();
            // Java的URL网络类可以让你通过URL去连接网络服务器并获取资源
            URL u = new URL(url);
            log.info("开始建立连接............");
            // HttpURLConnection是Java的标准类，它继承自URLConnection，可用于向指定网站发送GET请求、POST请求
            // 得到网络访问对象java.net.HttpURLConnection
            conn = (HttpURLConnection) u.openConnection();
            //跳过所有https证书认证
            boolean useHttps = url.startsWith("https");
            if (useHttps) {
                TrustAllHosts.trustAllHosts((HttpsURLConnection) conn);
                ((HttpsURLConnection) conn).setHostnameVerifier(TrustAllHosts.DO_NOT_VERIFY);
            }
            // 设置是否向HttpURLConnection输出
            conn.setDoInput(true);
            // 设置是否从httpUrlConnection读入
            conn.setDoOutput(true);
            // 连接超时时间
            conn.setConnectTimeout(connectTimeOut);
            // 读取超时时间
            conn.setReadTimeout(readTimeOut);
            // 请求方式
            conn.setRequestMethod(requestMethod.getRequestMethod());
            // 放入需要添加的请求头参数
            if (null != hearderParams) {
                for (Entry<String, String> entry : hearderParams.entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    conn.setRequestProperty(key, value);
                }
            }
            // 建立连接
            conn.connect();
            log.info("建立连接成功............");
            if (StringUtils.isNotEmpty(body)) {
                // 定义输出流，加入请求报文
                output = conn.getOutputStream();
                log.info("开始写入请求体............");
                output.write(body.getBytes(StandardCharsets.UTF_8));
                output.flush();
                log.info("写入请求体成功............");
            }
            Map<String, List<String>> responseHeader = conn.getHeaderFields();
            if (null != responseHeader && !responseHeader.isEmpty()) {
                for (String key : responseHeader.keySet()) {
                    if (null != key) {
                        log.info("响应头信息---------->: {}:{}", key, responseHeader.get(key).get(0));
                    } else {
                        log.info("响应头信息---------->: {}", responseHeader.get(null).get(0));
                    }
                }
            }
            int statusCode = conn.getResponseCode();
            log.info("请求响应码：{}", statusCode);
            if (200 == statusCode) {
                log.info("开始读取响应............");
                inputStream = conn.getInputStream();
                result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
                log.info("读取响应成功............");
                long end = System.currentTimeMillis();
                log.info("请求耗时----------->: {} ms", (end - start));
            } else {
                log.error("请求失败，HTTP 请求响应码不等于 200！！！");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != conn) {
                    conn.disconnect();
                }
                if (null != output) {
                    output.close();
                }
                if (null != inputStream) {
                    inputStream.close();
                }
            } catch (Exception e2) {
                // TODO: handle exception
                e2.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 使用GET方法下载文件
     *
     * @param url      请求路径
     * @param filePath 文件保存路径
     * @return 下载成功返回true，失败返回false
     */
    public static boolean downloadFileByGet(String url, String filePath) {
        return downloadFileByGet(url, new HashMap<>(), new HashMap<>(), filePath);
    }

    /**
     * 使用GET方法下载文件
     *
     * @param url           请求路径
     * @param hearderParams 请求头，如Content-type、Content-Length等
     * @param parameterMap  表单参数
     * @param filePath      文件保存路径
     * @return 下载成功返回true，失败返回false
     */
    public static boolean downloadFileByGet(String url, Map<String, String> hearderParams,
                                         Map<String, String> parameterMap, String filePath) {
        boolean isSuccess = false;
        String paramerterString = generateGetParameter(parameterMap);
        if (StringUtils.isNotEmpty(paramerterString)) {
            if (url.endsWith("?")) {
                url = url + paramerterString;
            } else {
                url = url + "?" + paramerterString;
            }
        }
        log.info("请求url------------->: {}", url);
        log.info("请求方法------------>: {}", RequestMethodEnum.GET.getRequestMethod());
        if (null != hearderParams && !hearderParams.isEmpty()) {
            for (String key : hearderParams.keySet()) {
                log.info("添加的请求头信息---------->: {}:{}", key, hearderParams.get(key));
            }
        }
        log.info("连接超时时间-------->: {} ms", CONNECT_TIME_OUT);
        log.info("读取响应超时时间----->: {} ms", READ_TIME_OUT);
        HttpURLConnection conn = null;
        InputStream inputStream = null;
        FileOutputStream fos = null;
        ByteArrayOutputStream bos = null;
        File fileExists = new File(filePath);
        try {
            long start = System.currentTimeMillis();
            if (!fileExists.exists()) {
                if (!mkdirParents(fileExists)) {
                    log.info("创建父级目录失败！");
                    return false;
                }
                if (!fileExists.createNewFile()) {
                    log.info("创建文件: {} 失败！", filePath);
                    return false;
                }
            }
            // Java的URL网络类可以让你通过URL去连接网络服务器并获取资源
            URL u = new URL(url);
            log.info("开始建立连接............");
            // HttpURLConnection是Java的标准类，它继承自URLConnection，可用于向指定网站发送GET请求、POST请求
            // 得到网络访问对象java.net.HttpURLConnection
            conn = (HttpURLConnection) u.openConnection();
            //跳过所有https证书认证
            boolean useHttps = url.startsWith("https");
            if (useHttps) {
                TrustAllHosts.trustAllHosts((HttpsURLConnection) conn);
                ((HttpsURLConnection) conn).setHostnameVerifier(TrustAllHosts.DO_NOT_VERIFY);
            }
            // 设置是否向HttpURLConnection输出
            conn.setDoInput(true);
            // 设置是否从httpUrlConnection读入
            conn.setDoOutput(true);
            // 连接超时时间
            conn.setConnectTimeout(CONNECT_TIME_OUT);
            // 读取超时时间
            conn.setReadTimeout(READ_TIME_OUT);
            // 请求方式
            conn.setRequestMethod(RequestMethodEnum.GET.getRequestMethod());
            // 放入需要添加的请求头参数
            if (null != hearderParams) {
                for (Entry<String, String> entry : hearderParams.entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    conn.setRequestProperty(key, value);
                }
            }
            // 建立连接
            conn.connect();
            log.info("建立连接成功............");
            Map<String, List<String>> responseHeader = conn.getHeaderFields();
            if (null != responseHeader && !responseHeader.isEmpty()) {
                for (String key : responseHeader.keySet()) {
                    if (null != key) {
                        log.info("响应头信息---------->: {}:{}", key, responseHeader.get(key).get(0));
                    } else {
                        log.info("响应头信息---------->: {}", responseHeader.get(null).get(0));
                    }
                }
            }
            int statusCode = conn.getResponseCode();
            log.info("请求响应码：{}", statusCode);
            if (200 == statusCode) {
                log.info("开始读取响应............");
                inputStream = conn.getInputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                bos = new ByteArrayOutputStream();
                while ((len = inputStream.read(buffer)) != -1) {
                    bos.write(buffer, 0, len);
                }
                byte[] getData = bos.toByteArray();
                fos = new FileOutputStream(fileExists);
                fos.write(getData);
                log.info("下载文件成功！");
                log.info("文件保存到：{}", filePath);
                log.info("读取响应成功............");
                long end = System.currentTimeMillis();
                log.info("请求耗时----------->: {} ms", (end - start));
                isSuccess = true;
            } else {
                log.error("请求失败，HTTP 请求响应码不等于 200！！！");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != conn) {
                    conn.disconnect();
                }
                if (null != inputStream) {
                    inputStream.close();
                }
                if (null != fos) {
                    fos.close();
                }
                if (null != bos) {
                    bos.close();
                }
            } catch (Exception e2) {
                // TODO: handle exception
                e2.printStackTrace();
            }
        }
        return isSuccess;
    }

    /**
     * 根据map生成get请求参数，格式 A=a&B=b
     *
     * @param parameterMap 请求参数map
     * @return get请求参数url
     */
    private static String generateGetParameter(Map<String, String> parameterMap) {
        StringBuilder sb = new StringBuilder();
        if (null != parameterMap) {
            Set<String> keySet = parameterMap.keySet();
            int index = 1;
            for (String key : keySet) {
                String value = parameterMap.get(key);
                try {
                    value = URLEncoder.encode(value, String.valueOf(StandardCharsets.UTF_8));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                if (index == keySet.size()) {
                    sb.append(key).append("=").append(value);
                } else {
                    sb.append(key).append("=").append(value).append("&");
                }
                index++;
            }
        }
        return sb.toString();
    }

    /**
     * 判断文件的父级目录是否存在，不存在则创建
     *
     * @param file 文件
     * @return true 父级目录存在或创建父级目录成功， false创建父级目录失败
     */
    public static boolean mkdirParents(File file) {
        if (!file.getParentFile().exists()) {
            return file.getParentFile().mkdirs();
        } else {
            return true;
        }
    }
}
